package com.hevery.security.browser;

import com.hevery.security.browser.authentication.CustomAuthenticationFailureHandler;
import com.hevery.security.browser.authentication.CustomAuthenticationSuccessHandler;
import com.hevery.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
import com.hevery.security.core.properties.SecurityProperties;
import com.hevery.security.core.validate.code.SmsCodeFilter;
import com.hevery.security.core.validate.code.ValidateCodeFilter;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;


/**
 * @author: zhy
 * @description:
 * @date: created on 2017/10/22
 * @modified by:
 */
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private SecurityProperties securityProperties;

    /**注入自定义的认证成功处理器*/
    @Autowired
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    /**注入自定义的认证失败处理器*/
    @Autowired
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    /**注入数据源，有关数据源的具体配置在application.properties中*/
    @Autowired
    private DataSource dataSource;

    /**TokenRepository在数据库中查找Token，并查找到UserName以后，我们还需要调用UserDetailsService来用这个用户名来登录*/
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;

    /**
     * 类BCryptPasswordEncoder就是PasswordEncoder接口的一个实现类。
     * 是SpringSecurity中提供的一种密码加密的方法。如果想自定义加密规则，
     * 可以自己定义一个实现PasswordEncoder接口的类，重写里面的encode、match方法。
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 为RememberMeServices注册一个TokenRepository，方便对数据库的中token信息进行存取
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //在启动的时候系统会自动打个为我们创建一张表用于存储用户名和Token
//        tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        //设置验证码的失败处理器
        //validateCodeFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
        //validateCodeFilter.setSecurityProperties(securityProperties);
        //调用Spring初始化validateCodeFilter的方法
        //validateCodeFilter.afterPropertiesSet();

        SmsCodeFilter smsCodeFilter = new SmsCodeFilter();
        //设置验证码的失败处理器
        smsCodeFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
        smsCodeFilter.setSecurityProperties(securityProperties);
        //调用Spring初始化validateCodeFilter的方法
        smsCodeFilter.afterPropertiesSet();




        http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
             //将验证码过滤器放在UsernamePasswordAuthenticationFilter过滤器的前面
            .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
            .formLogin()/**使用表单登录的方式进行认证*/
                .loginPage("/authentication/require")
                .loginProcessingUrl("/authentication/form")
                //指定登录成功的处理器
                .successHandler(customAuthenticationSuccessHandler)
                //指定登录失败的处理器
                .failureHandler(customAuthenticationFailureHandler)
                .and()
                //记住我的配置
            .rememberMe()
                //配置tokenRepository
                .tokenRepository(persistentTokenRepository())
                //配置过期的秒数
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
                //配置userDetailsService，最总拿到用户名以后用这个做登录
                .userDetailsService(userDetailsService)
//              http.httpBasic()/**使用httpBasic登录的方式进行认证*/
                .and()
                //对请求授权
                .authorizeRequests()
                .antMatchers("/authentication/require", securityProperties.getBrowser().getLoginPage(), "/code/*").permitAll()
                //任何请求
                .anyRequest()
                //都需要身份认证
                .authenticated()
                .and()
                .csrf().disable()
                .apply(smsCodeAuthenticationSecurityConfig);

    }
}
